home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / castools.zip / GARBAGEC.C < prev    next >
Text File  |  1990-01-18  |  8KB  |  273 lines

  1.  
  2.  
  3. /*
  4.    GARBAGEC.C  Function PbGarbageCollect:  eliminates the blank areas of a
  5.       phonebook file.
  6.  
  7.    This function reduces to 0 the unused bytes in a phonebook.
  8.  
  9.    INPUT:  Name of phonebook file to process.
  10.  
  11.    OUTPUT: If successful, returns 1.  Otherwise sets Pberrno and returns 0.
  12. */
  13.  
  14. #include <stdlib.h>
  15. #include <malloc.h>
  16. #include <stdio.h>
  17. #include <io.h>
  18. #include <phonebk.h>
  19. #include <ctype.h>
  20. #include <dos.h>
  21.  
  22. int pascal PbGarbageCollect(char *PbFilename)
  23. {
  24.   int retval = 0;                       /* default (failed) return value */
  25.   PB *SourcePb = NULL,
  26.      *NewPb = NULL;
  27.   long     PbSize, unused, avail;
  28.   int      filenum;
  29.   LONGWORD clusters,
  30.            sectors,
  31.            bytes,          /* need these temp's to prevent integer overflow */
  32.            next_offset;
  33.  
  34.   struct diskfree_t diskspace;          /* for call to _dos_getdiskfree */
  35.   unsigned drive;                       /* for call do _dos_getdiskfree */
  36.  
  37.   int i, j;                             /* loop counters */
  38.   size_t red, writ;                     /* returns for fread() and fwrite() */
  39.  
  40.   char *temp_name = NULL;               /* tmpnam() allocates space for this */
  41.  
  42.   LONGWORD *offsets = NULL; /* will hold 1000 bytes at a time of offset array */
  43.   fpos_t source_pos,      /* for pushing and popping fpos in sourcePb */
  44.          o_dest_pos,      /* for pushing and popping fpos in destPb's offsets */
  45.          e_dest_pos;      /* for pushing and popping fpos in destPb's entries */
  46.  
  47.   WORD entry_size,
  48.        next_entry_size;
  49.   char *entry_buffer = NULL;
  50.  
  51.   Pberrno = 0;            /* Initially, always reset */
  52.  
  53.   /* First, check params */
  54.   if (PbFilename == NULL) {
  55.     Pberrno = INVALIDPARAMETER;
  56.     return(NULL);
  57.   }
  58.  
  59.   /* Open the named phonebook */
  60.   SourcePb = PbOpenPhonebook(NULL, PbFilename);
  61.   if (!SourcePb) {
  62.     return(0);       /* PbOpenPhonebook has set Pberrno */
  63.   }
  64.  
  65.   /* We need to know: size of the phonebook to clean,
  66.                       # of unused (garbage) bytes in that phonebook,
  67.                       # of bytes available on disk,
  68.                       # of bytes as a safety margin.                    */
  69.  
  70.   filenum = fileno(SourcePb->fp);
  71.   PbSize = filelength(filenum);
  72.   if (PbSize == -1L) {
  73.     goto closeup;
  74.   }
  75.   unused = SourcePb->header.FreeBytes;
  76.  
  77.   /* Determine the drive number for call to _dos_getdiskfree */
  78.   if (PbFilename[1] == ':') {
  79.     PbFilename[0] = toupper(PbFilename[0]);
  80.     drive = PbFilename[0] - 'A' + 1;
  81.   }
  82.   else drive = 0;
  83.  
  84.   if (_dos_getdiskfree(drive, &diskspace)) {
  85.     goto closeup;
  86.   }
  87.   avail = (clusters = diskspace.avail_clusters) *
  88.           (sectors = diskspace.sectors_per_cluster) *
  89.           (bytes = diskspace.bytes_per_sector);
  90.  
  91.   /* If not enough diskspace for operation, set Pberrno and get out */
  92.   if (PbSize - unused > avail - DISKSPACEMARGIN) {
  93.     Pberrno = OUTOFDISKSPACE;
  94.     goto closeup;
  95.   }
  96.  
  97.   /* Create a new phonebook with a temporary name, and copy entries into it */
  98.   temp_name = tmpnam(NULL);
  99.   NewPb = PbCreatePhonebook(NULL, temp_name);
  100.   if (!NewPb) {
  101.     goto closeup;           /* PbCreatePhonebook has set Pberrno */
  102.   }
  103.   if (fseek(SourcePb->fp, 160L, SEEK_SET)) {
  104.     Pberrno = FSEEKERROR;
  105.     goto closeup;
  106.   }
  107.   if (fseek(NewPb->fp, 160L, SEEK_SET)) {
  108.     Pberrno = FSEEKERROR;
  109.     goto closeup;
  110.   }
  111.   if (fgetpos(NewPb->fp, &o_dest_pos)) { /* save position in offset array */
  112.     goto closeup;
  113.   }
  114.   if (fseek(NewPb->fp, 4160L, SEEK_SET)) { /* get ready for copying 1st entry */
  115.     Pberrno = FSEEKERROR;
  116.     goto closeup;
  117.   }
  118.   if (fgetpos(NewPb->fp, &e_dest_pos)) {  /* save position in entry section */
  119.     goto closeup;
  120.   }
  121.   next_offset = 4160L;                   /* offset of next entry that goes in */
  122.   entry_buffer = (char *)malloc(entry_size = sizeof(PBEFIXED));
  123.   if (!entry_buffer) {
  124.     Pberrno = OUTOFMEM;
  125.     goto closeup;
  126.   }
  127.   offsets = (LONGWORD *)malloc(1000);
  128.   if (!offsets) {
  129.     Pberrno = OUTOFMEM;
  130.     goto closeup;
  131.   }
  132.  
  133.   /* Read and process the 4K offset array, 1K at a time */
  134.   for (i=0; i<4; i++) {
  135.     red = fread(offsets, sizeof(LONGWORD), 250, SourcePb->fp);
  136.     if (red != 250) {
  137.       Pberrno = CANTREAD;
  138.       goto closeup;
  139.     }
  140.     if (fgetpos(SourcePb->fp, &source_pos)) {    /* save pos in source */
  141.       goto closeup;
  142.     }
  143.     if (fsetpos(NewPb->fp, &e_dest_pos)) {  /* go to last position in entries*/
  144.       goto closeup;
  145.     }
  146.  
  147.     /* For each 1/4 of the offset array, scan it for non-NULL entries */
  148.     for (j=0; j<250; j++) {
  149.       if (offsets[j]) {
  150.         NewPb->header.entries++;
  151.  
  152.         /* Get the entry in two reads: 1st, the fixed part, then the rest */
  153.         if (fseek(SourcePb->fp, offsets[j], SEEK_SET)) {
  154.           Pberrno = FSEEKERROR;
  155.           goto closeup;
  156.         }
  157.         red = fread(entry_buffer, 1, sizeof(PBEFIXED), SourcePb->fp);
  158.         if (red != sizeof(PBEFIXED)) {
  159.           Pberrno = CANTREAD;
  160.           goto closeup;
  161.         }
  162.  
  163.         /* Reallocate the entry_buffer only if new entry is larger than last */
  164.         next_entry_size = ((PBEFIXED *)entry_buffer)->length;
  165.         if (entry_size < next_entry_size) {
  166.           entry_buffer = (char *)realloc(entry_buffer, next_entry_size);
  167.           if (!entry_buffer) {
  168.             Pberrno = OUTOFMEM;
  169.             goto closeup;
  170.           }
  171.         }
  172.         entry_size = next_entry_size;
  173.         red = fread(entry_buffer + sizeof(PBEFIXED), 1,
  174.               entry_size - sizeof(PBEFIXED),
  175.               SourcePb->fp);
  176.         if (red != entry_size - sizeof(PBEFIXED)) {
  177.           Pberrno = CANTREAD;
  178.           goto closeup;
  179.         }
  180.  
  181.         /* Now copy that entry to end of new phonebook, setting the new offset*/
  182.         offsets[j] = next_offset;
  183.         next_offset += entry_size;
  184.         writ = fwrite(entry_buffer, 1, entry_size, NewPb->fp);
  185.         if (writ != entry_size) {
  186.           Pberrno = CANTWRITE;
  187.           goto closeup;
  188.         }
  189.       }     /* end: if (offsets[j]) */
  190.     }       /* end: for all 250 offsets in this 1/4 */
  191.  
  192.     if (fgetpos(NewPb->fp, &e_dest_pos)) {      /* save position for entries */
  193.       goto closeup;
  194.     }
  195.     if (fsetpos(NewPb->fp, &o_dest_pos)) {      /* go to last pos in offsets */
  196.       goto closeup;
  197.     }
  198.  
  199.     /*  Write the updated 1/4 section of the offset array */
  200.     writ = fwrite(offsets, sizeof(LONGWORD), 250, NewPb->fp);
  201.     if (writ != 250) {
  202.       Pberrno = CANTWRITE;
  203.       goto closeup;
  204.     }
  205.     if (fgetpos(NewPb->fp, &o_dest_pos)) {      /* save pos in offsets */
  206.       goto closeup;
  207.     }
  208.     if (fsetpos(SourcePb->fp, &source_pos)) {   /* return to pos for entries */
  209.       goto closeup;
  210.     }
  211.   } /* end of 4 reads of 1K each */
  212.  
  213.   /* If # of records in new phonebook don't match # of records in old, error */
  214.   if (NewPb->header.entries != SourcePb->header.entries) {
  215.     Pberrno = INCONSISTENTPBH;       /* this is a warning only */
  216.   }
  217.  
  218.   /* One more detail: have to copy the header of the source Pb onto the copy */
  219.   rewind(NewPb->fp);
  220.   SourcePb->header.FreeBytes = 0;   /* the only field different */
  221.   writ = fwrite(&SourcePb->header, 1, sizeof(PBH), NewPb->fp);
  222.   if (writ != sizeof(PBH)) {
  223.     Pberrno = CANTWRITE;
  224.     goto closeup;
  225.   }
  226.  
  227.   /* If we've reached here, all is well: out with the old, in with the new! */
  228.  
  229.   /* First, close up the files and null out the phonebook ptrs */
  230.   if (fclose(SourcePb->fp)) {
  231.     Pberrno = CANTCLOSE;
  232.   }
  233.   free(SourcePb);
  234.   SourcePb = NULL;
  235.   if (fclose(NewPb->fp)) {
  236.     Pberrno = CANTCLOSE;
  237.   }
  238.   free(NewPb);
  239.   NewPb = NULL;
  240.  
  241.   /* Finally, delete the old and rename the new to the old name */
  242.   if (remove(PbFilename)) {
  243.     goto closeup;
  244.   }
  245.   if (rename(temp_name, PbFilename)) {
  246.     goto closeup;
  247.   }
  248.   retval = 1;                        /* Success! */
  249. closeup:
  250.   if (temp_name) {
  251.     free(temp_name);
  252.   }
  253.   if (entry_buffer) {
  254.     free(entry_buffer);
  255.   }
  256.   if (offsets) {
  257.     free(offsets);
  258.   }
  259.   if (SourcePb) {
  260.     if (fclose(SourcePb->fp)) {
  261.       Pberrno = CANTCLOSE;
  262.     }
  263.     free(SourcePb);
  264.   }
  265.   if (NewPb) {
  266.     if (fclose(NewPb->fp)) {
  267.       Pberrno = CANTCLOSE;
  268.     }
  269.     free(NewPb);
  270.   }
  271.   return(retval);
  272. }
  273.